package org.jparrow.framework.util;

import java.io.File;
import java.io.FileFilter;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 类操作工具类
 * 
 * @author supda
 * @date 2017-3-25
 *
 */
public final class ClassUtil {

	private static final Logger LOGGER = 
			LoggerFactory.getLogger(ClassUtil.class);
			
	/**
	 * 获取类加载器
	 */
	public static ClassLoader getClassLoader(){
		return Thread.currentThread().getContextClassLoader();
	}
	
	/**
	 *加载类
	 * className:类名
	 * isInitialized：是否初始化（是否执行类的静态代码块）
	 */
	public static Class<?> loadClass(String className, boolean isInitialized){
		Class<?> cls;
		try{
			cls = Class.forName(className, isInitialized, getClassLoader());
		}catch(ClassNotFoundException e){
			LOGGER.error("load class failure", e);
			throw new RuntimeException(e);
		}
		return cls;
	}
	
	/**
	 *加载类
	 */
	public static Class<?> loadClass(String className){
		return loadClass(className, true);
	}
	
	/**
	 * 获取指定包名下的所有的类,第三方类库两种形式
	 * 	1.文件类
	 * 	2.jar包
	 */
	public static Set<Class<?>> getClassSet(String packageName){
		Set<Class<?>> classSet = new HashSet<Class<?>>();
		try{
			Enumeration<URL> urls = getClassLoader().
					getResources(packageName.replace(".", "/"));
			
			System.out.println("加载："+packageName+" 下的所有类");
			
			while(urls.hasMoreElements()){
				URL url = urls.nextElement();
				if(url != null){
					String protocol = url.getProtocol();
					if(protocol.equals("file")){
						
						System.out.println("加载路径："+CodecUtil.decodeURL(url.getPath()));
						
						String packagePath = CodecUtil.decodeURL(url.getPath());
						addClass(classSet, packagePath, packageName);
					}else if(protocol.equals("jar")){
						JarURLConnection jarURLConnection = 
								(JarURLConnection)url.openConnection();
						if(jarURLConnection != null){
							JarFile jarFile = jarURLConnection.getJarFile();
							if(jarFile!=null){
								Enumeration<JarEntry> jarEntries = jarFile.entries();
								while(jarEntries.hasMoreElements()){
									JarEntry jarEntry = jarEntries.nextElement();
									String jarEntryName = jarEntry.getName();
									if(jarEntryName.endsWith("class")) {
										String className = jarEntryName.substring(0, 
												jarEntryName.lastIndexOf(".")).
												replaceAll("/", ".");
										doAddClass(classSet, className);
									}
								}
							}
						}
					}	
					
				}
			}
			
		}catch(Exception e){
			LOGGER.error("get class set failure", e);
			throw new RuntimeException(e);
		}
		return classSet;
	}

	private static void addClass(Set<Class<?>> classSet, String packagePath,
			String packageName) {
		
		File[] files = new File(packagePath).listFiles(new FileFilter() {
			
			@Override
			public boolean accept(File file) {
				return (file.isFile() && file.getName().endsWith(".class"))
						|| file.isDirectory();
			}
		});
		
		for(File file : files ) {
			String fileName = file.getName();
			System.out.println("加载文件："+fileName);
			if(file.isFile()) {
				String className = fileName.substring(0, fileName.lastIndexOf("."));
				if(StringUtil.isNotEmpty(packageName)){
					className = packageName + "." + className;
				}
				doAddClass(classSet, className);
			}else{
				String subPackagePath = fileName;
				if(StringUtil.isNotEmpty(packagePath)) {
					subPackagePath = packagePath+ "/" + subPackagePath;
				}
				String subPackageName = fileName;
				if(StringUtil.isNotEmpty(packageName)){
					subPackageName = packageName + "." + subPackageName;
				}
				addClass(classSet, subPackagePath, subPackageName);
			}
		}
		
	}

	private static void doAddClass(Set<Class<?>> classSet, String className) {
		Class<?> cls = loadClass(className, false);
		classSet.add(cls);
	}
	
}
